#include <QIODevice>
#include <QPointer>

#include "QRO_p.h"

QVariant invokeMetaMethod(QObject* o,QMetaMethod method,QVariantList param){
    QVariant ret;
    QGenericArgument args[10];
    for(int i = 0;i<param.size();i++){
        auto& value = param[i];
        auto paramType = method.parameterType(i);
        value.convert(QMetaType(paramType));
        args[i] = QGenericArgument(value.typeName(),value.data());
    }


    QGenericReturnArgument retArg;
    auto retType = method.returnMetaType();
    void* retPtr = NULL;
    if(retType.id() != QMetaType::Void)
    {
        retPtr = retType.create();
        retArg = QGenericReturnArgument(retType.name(),retPtr);
    }

    method.invoke(o,retArg,args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);

    if(retType.id() != QMetaType::Void)
    {
        ret = QVariant(retType,retPtr);
        retType.destroy(retPtr);
    }

    return ret;
}

QVariant uncompressVariant(QByteArray bta){
    bta = qUncompress(bta);

    QVariant ret;

    QDataStream stream(&bta,QIODevice::ReadOnly);
    stream >> ret;

    return ret;
}

QByteArray compressVariant(QVariant packet){
    QByteArray array;
    QDataStream stream(&array,QIODevice::WriteOnly);

    stream << packet;
    array = qCompress(array);
    return array;
}

static void serilizeImp(QVariant &output,const QVariant& input, const std::function<ObjectID (QObject *)> &convertor)
{
    auto meta = input.metaType();
    auto flag = meta.flags();
    if(flag & QMetaType::PointerToQObject)
    {
        auto obj = input.value<QObject*>();
        auto id = convertor(obj);
        output = QVariant::fromValue(id);
    }
    else if(flag & QMetaType::TrackingPointerToQObject)
    {
        auto obj = input.value<QPointer<QObject>>();
        auto id = convertor(obj);
        output = QVariant::fromValue(id);
    }
    else{
        output = input;
    }
}

QVariant serilizeValue(QVariant value, const std::function<ObjectID (QObject *)> &convertor)
{
    QVariant ret;

    serilizeImp(ret,value,convertor);

    return ret;
}

static void unserilizeImp(QVariant& output,const QVariant& input, const std::function<QObject *(ObjectID)> &convertor, int targetType)
{
    auto meta = QMetaType(targetType);
    auto flag = meta.flags();
    if(flag & QMetaType::PointerToQObject)
    {
        auto id = input.value<ObjectID>();
        auto obj = convertor(id);
        output = QVariant::fromValue(obj);
        output.convert(meta);
    }
    else if(flag & QMetaType::TrackingPointerToQObject)
    {
        auto id = input.value<ObjectID>();
        auto obj = convertor(id);
        output = QVariant::fromValue(QPointer<QObject>(obj));
        output.convert(meta);
    }
    else{
        output = input;
    }
}

QVariant unserilizeValue(QVariant value, const std::function<QObject *(ObjectID)> &convertor, int targetType)
{
    QVariant ret;
    unserilizeImp(ret,value,convertor,targetType);
    return ret;
}
